from PySide6 import QtCore, QtWidgets, QtGui
from PySide6.QtCore import Qt
from PySide6.QtGui import QPainter, QWheelEvent
from PySide6.QtWidgets import QGraphicsView

from 程序核心代码.mvvm重构.基础定义.基础图形项.节点图形项.容器图形项 import 容器组件基础图形项
from 程序核心代码.mvvm重构.基础定义.基础图形项.节点图形项.带容器带组件带布局的节点图形项 import 带容器带组件带布局的节点图形项
from 程序核心代码.mvvm重构.基础定义.基础图形项.节点图形项.节点图形项 import 节点基础图形项
from 程序核心代码.图形界面.窗口及画布场景.opengl加速 import OpenGL加速组件

缩放最小值 = -0.95
缩放最大值 = 2.0
import logging
# 配置日志记录器
logging.basicConfig(level=logging.WARNING, format='%(asctime)s - %(levelname)s - %(message)s')
日志记录器 = logging.getLogger(__name__)

class 场景显示窗口定义(QGraphicsView):
    节点被选中 = QtCore.Signal(str)
    节点选择改变 = QtCore.Signal(list, list)
    节点双击 = QtCore.Signal(str)
    def __init__(self, 场景=None, 父对象=None):
        if 场景 is None:
            raise ValueError("视图管理器必须传入场景或者容器")
        super().__init__(scene=场景, parent=父对象)
        self.调试模式 = False  # 调试模式开关
        # 设置日志等级
        if self.调试模式:
            日志记录器.setLevel(logging.DEBUG)
            日志记录器.debug(f"{self.__class__.__name__}调试模式已开启")
        else:
            日志记录器.setLevel(logging.ERROR)  # 只显示警告及以上级别的日志

        #起始接口.选择框 = QRubberBand(QRubberBand.Rectangle, 起始接口)
        self._初始化视图设置()
        self.中键拖动标识 = False  # 标记是否正在拖动
        self.拖动起始位置 = None  # 初始化拖动起始位置

        # 定义缩放限制
        self.min_zoom = 0.5
        self.max_zoom = 2.0

        # 设置定时器
        # 起始接口.update_timer = QTimer(起始接口)
        # 起始接口.update_timer.timeout.connect(起始接口.update_scene)
        # 起始接口.update_timer.setInterval(16)  # 大约60帧每秒
        self._鼠标按下位置 = QtCore.QPoint(int(self.width() / 2),
                                           int(self.height() / 2))
        self._场景范围 = QtCore.QRectF(
            0, 0, self.size().width(), self.size().height())
        self._橡皮筋 = QtWidgets.QRubberBand(
            QtWidgets.QRubberBand.Rectangle, self
        )
        self._橡皮筋.isActive = False
        self._最后大小 = self.size()
        self.左键状态 = False
        self.右键状态 = False
        self.中键状态 = False
        self._原始位置 = None

        self._之前选择节点 = []
        self._节点位置 = {}

    def _初始化视图设置(self):
        # 设置视口为 QOpenGLWidget
        gl_widget = OpenGL加速组件(self)
        self.setViewport(gl_widget)
        self.setDragMode(QGraphicsView.ScrollHandDrag)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)  # 优化视图更新
        # 设置优化标志
        self.setOptimizationFlag(QGraphicsView.DontSavePainterState)
        self.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.TextAntialiasing)

    def wheelEvent(self, 事件: QWheelEvent):
        滚动增量 = 事件.angleDelta().y()   # 获取滚动增量
        self._设置查看器缩放(滚动增量, 位置=事件.position().toPoint())

    def 调整视图(self, 滚动方向):
        """
        调整视图缩放
        """
        缩放步长 = 1.1  # 减小缩放步长
        缩放因子 = 缩放步长 if 滚动方向 > 0 else 1 / 缩放步长
        当前变换矩阵 = self.transform()
        当前缩放级别 = 当前变换矩阵.m11()

        # 限制缩放范围
        if (滚动方向 > 0 and 当前缩放级别 < self.max_zoom) or (滚动方向 < 0 and 当前缩放级别 > self.min_zoom):
            self.scale(缩放因子, 缩放因子)  # 调整缩放
            self.resetCachedContent()

    def mousePressEvent(self, event):
        """处理鼠标按下事件以开始拖动场景"""
        if event.button() == QtCore.Qt.LeftButton: # 左键按下
            self.左键状态 = True
        elif event.button() == QtCore.Qt.RightButton:# 右键按下
            self.右键状态 = True
        elif event.button() == QtCore.Qt.MiddleButton:  # 中键按下
            self.中键状态 = True

        #
        # if event.button() == Qt.MiddleButton:  # 判断是否按下了中键
        #     起始接口.setDragMode(QGraphicsView.NoDrag)  # 设置拖拽模式为滚动手柄拖拽
        #     起始接口.中键拖动标识 = True  # 开始拖动
        #     起始接口.拖动起始位置 = 起始接口.mapToScene(event.pos())  # 记录拖动起始位置
        #     起始接口.setCursor(Qt.ClosedHandCursor)  # 设置鼠标指针样式为抓手
        #
        # elif event.button() == Qt.LeftButton:
        #     起始接口.setDragMode(QGraphicsView.RubberBandDrag)  # 设置拖拽模式为橡皮筋拖拽
        self._原始位置 = event.pos()
        self._鼠标按下位置 = event.pos()
        self._鼠标按下位置 = event.pos()
        self._之前选择节点= self.selected_items()
        映射位置 = self.mapToScene(event.pos())
        附近项目列表 = self._附近项目(映射位置, None, 1, 1)
        # 显示选择标记。
        if self.左键状态 and not 附近项目列表:
            矩形 = QtCore.QRect(self._鼠标按下位置, QtCore.QSize())
            矩形 = 矩形.normalized()
            映射矩形 = self.mapToScene(矩形).boundingRect()
            self.scene().update(映射矩形)
            self._橡皮筋.setGeometry(矩形)
            self._橡皮筋.isActive = True


        super().mousePressEvent(event)


    def mouseMoveEvent(self, event):
        """处理鼠标移动事件以拖动场景"""
        # if 起始接口.中键拖动标识 and 起始接口.拖动起始位置 is not None:
        #     鼠标按下位置 = 起始接口.mapToScene(起始接口._鼠标按下位置)
        #     鼠标当前位置 = 起始接口.mapToScene(event.pos())
        #     delta = 鼠标按下位置 - 鼠标当前位置
        #     起始接口._设置查看器平移(delta.x(), delta.y())
        if self.中键状态 :
            上一个位置 = self.mapToScene(self._鼠标按下位置)
            当前位置 = self.mapToScene(event.pos())
            delta = 上一个位置 - 当前位置
            self._设置查看器平移(delta.x(), delta.y())
        if self.左键状态 and self._橡皮筋.isActive:
            矩形 = QtCore.QRect(self._原始位置, event.pos()).normalized()
            # 如果橡皮筋太小，则不显示。
            if max(矩形.width(), 矩形.height()) > 5:
                if not self._橡皮筋.isVisible():
                    self._橡皮筋.show()
                映射矩形 = self.mapToScene(矩形).boundingRect()
                路径 = QtGui.QPainterPath()
                路径.addRect(映射矩形)
                self._橡皮筋.setGeometry(矩形)
                self.scene().setSelectionArea(路径)
                self.scene().update(映射矩形)

        self._鼠标按下位置 = event.pos()
        super().mouseMoveEvent(event)


    def mouseReleaseEvent(self, event):
        """处理鼠标释放事件以结束拖动场景"""
        if event.button() == QtCore.Qt.LeftButton:
            self.左键状态 = False
        elif event.button() == QtCore.Qt.RightButton:
            self.右键状态 = False
        elif event.button() == QtCore.Qt.MiddleButton:
            self.中键状态 = False
            #起始接口.setCursor(Qt.ArrowCursor)  # 恢复鼠标指针样式为箭头
            # 隐藏选择标记
        if self._橡皮筋.isActive:
            self._橡皮筋.isActive = False
            if self._橡皮筋.isVisible():
                矩形 = self._橡皮筋.rect()
                映射矩形 = self.mapToScene(矩形).boundingRect()
                self._橡皮筋.hide()

                #矩形 = QtCore.QRect(起始接口._原始位置, event.pos()).normalized()
                # 矩形项目 = 起始接口.scene().items(
                #     起始接口.mapToScene(矩形).boundingRect()
                # )
                # 节点ID列表 = []
                # for item in 矩形项目:
                #     if isinstance(item, 节点基础图形项):
                #         节点ID列表.append(item.节点定义.id)
                #
                # # 发射节点选择信号。
                # if 节点ID列表:
                #     之前ID = [
                #         n.id for n in 起始接口._之前选择节点
                #         if not n.selected
                #     ]
                #     起始接口.节点被选中.emit(节点ID列表[0])
                #     起始接口.节点选择改变.emit(节点ID列表, 之前ID)

                self.scene().update(映射矩形)
                return

        super().mouseReleaseEvent(event)
        self.setDragMode(QGraphicsView.NoDrag)

    def update_scene(self):
        # 定期更新场景
        if self.scene():
            self.scene().update()

    def get_zoom(self):
        """
        返回视图当前的缩放级别。

        返回：
            float: 缩放级别数值。
        """
        transform = self.transform()
        cur_scale = (transform.m11(), transform.m22())
        return float('{:0.2f}'.format(cur_scale[0] - 1.0))


    def _设置查看器平移(self, pos_x, pos_y):
        """
        将查看器设置为平移模式。

        Args:
            pos_x (float): x 位置。
            pos_y (float): y 位置。
        """
        self._场景范围.adjust(pos_x, pos_y, pos_x, pos_y)
        self._更新场景()
    def _更新场景(self):
        """
        重新绘制场景。
        """
        self.setSceneRect(self._场景范围)
        self.fitInView(self._场景范围, QtCore.Qt.KeepAspectRatio)
    def _设置查看器缩放(self, 值, 灵敏度=None, 位置=None):
        """
        设置缩放级别。

        Args:
            值 (float): 缩放因子。
            灵敏度 (float): 缩放灵敏度。
            位置 (QtCore.QPoint): 映射位置。
        """
        if 位置:
            位置 = self.mapToScene(位置)
        if 灵敏度 is None:
            缩放 = 1.001 ** 值
            self.scale(缩放, 缩放, 位置)
            return

        if 值 == 0.0:
            return

        缩放 = (0.9 + 灵敏度) if 值 < 0.0 else (1.1 - 灵敏度)
        当前缩放 = self.get_zoom()
        if 缩放最小值 >= 当前缩放:
            if 缩放 == 0.9:
                return
        if 缩放最大值 <= 当前缩放:
            if 缩放 == 1.1:
                return
        self.scale(缩放, 缩放, 位置)
    def scale(self, sx, sy, 位置=None):
        缩放 = [sx, sx]
        中心 = 位置 or self._场景范围.center()
        w = self._场景范围.width() / 缩放[0]
        h = self._场景范围.height() / 缩放[1]
        self._场景范围 = QtCore.QRectF(
            中心.x() - (中心.x() - self._场景范围.left()) / 缩放[0],
            中心.y() - (中心.y() - self._场景范围.top()) / 缩放[1],
            w, h
        )
        self._更新场景()
    def resizeEvent(self, event):
        """
        重写窗口大小事件。

        这将更新场景大小，并根据窗口大小调整缩放级别。
        """
        w, h = self.size().width(), self.size().height()
        if 0 in [w, h]:
            self.resize(self._最后大小)
        delta = max(w / self._最后大小.width(), h / self._最后大小.height())
        self._设置查看器缩放(delta)
        self._最后大小 = self.size()
        super().resizeEvent(event)

    def selected_items(self):
        """
        返回场景中选择的图形项目。

        返回：
            tuple(list[AbstractNodeItem], list[Pipe]):
                选择（节点项，管道项）。
        """
        节点 = []

        for item in self.scene().selectedItems():
            if isinstance(item, 节点基础图形项):
                节点.append(item)
        return 节点
    def _附近项目(self, 位置, 项目类型=None, 宽度=20, 高度=20):
        """
        从指定位置、宽度和高度区域过滤节点图项目。

        Args:
            位置 (QtCore.QPoint): 场景位置。
            项目类型: 过滤项目类型。（可选）
            宽度 (int): 宽度区域。
            高度 (int): 高度区域。

        Returns:
            list: 场景中的qgraphics项目。
        """
        x, y = 位置.x() - 宽度, 位置.y() - 高度
        矩形 = QtCore.QRectF(x, y, 宽度, 高度)
        项目 = []
        #排除 = [起始接口._实时管道, 起始接口._切割管道]
        for item in self.scene().items(矩形):
            # if item in 排除:
            #     continue
            if isinstance(item, 容器组件基础图形项):
                continue
            if isinstance(item, 带容器带组件带布局的节点图形项):
                continue

            if not 项目类型 or isinstance(item, 项目类型):
                项目.append(item)
        # if 项目:
        #     print(项目)
        return 项目
